package com.stark.wechat.service.message;

import com.dtflys.forest.Forest;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.stark.wechat.core.Config;
import com.stark.wechat.service.message.model.*;
import com.stark.wechat.util.JacksonUtils;
import com.stark.wechat.util.ResponseUtils;
import lombok.Builder;
import lombok.extern.slf4j.Slf4j;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static java.util.Objects.requireNonNull;

/**
 * 模板消息接口
 *
 * @author <a href="mailto:mengbin@hotmail.com">Ben</a>
 * @version 1.0.0
 * @see <a href="https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Template_Message_Interface.html">能力接入 / 模板消息 / 模板消息介绍</a>
 * @since 2024/5/27
 */
@Builder
@Slf4j
public class TemplateService {

    private final Config config;

    /**
     * 发送模板消息
     * <p>本接口用于向用户发送模板消息
     *
     * @param accessToken     接口调用凭证，可使用 <a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AccessToken.html">access_token</a>、<a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AuthorizerAccessToken.html">authorizer_access_token</a>
     * @param templateMessage 模板消息
     * @return 模板消息ID
     * @see <a href="https://developers.weixin.qq.com/doc/service/api/notify/template/api_sendtemplatemessage.html">基础消息与订阅通知 / 模板消息 / 发送模板消息</a>
     */
    public Long sendTemplateMessage(String accessToken, TemplateMessage templateMessage) {
        requireNonNull(accessToken);
        requireNonNull(templateMessage);

        String body = JacksonUtils.serialize(templateMessage);
        String responseStr = Forest
                .post(config.getWechatGateway() + "/cgi-bin/message/template/send")
                .addQuery("access_token", accessToken)
                .addBody(body)
                .contentTypeJson()
                .connectTimeout(config.getConnectTimeout())
                .readTimeout(config.getReadTimeout())
                .executeAsString();
        JsonNode root = JacksonUtils.readTree(responseStr);
        ResponseUtils.findError("发送模板消息", root, CODE_MAP.get("sendTemplateMessage"));
        return root.get("msgid").asLong();
    }

    /**
     * 选用模板
     * <p>本接口用于从类目模板库或行业模板库添加模板获得模板ID
     *
     * @param accessToken    接口调用凭证，可使用 <a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AccessToken.html">access_token</a>、<a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AuthorizerAccessToken.html">authorizer_access_token</a>
     * @param requestPayload 请求体
     * @return 模板ID
     * @see <a href="https://developers.weixin.qq.com/doc/service/api/notify/template/api_addtemplate.html">基础消息与订阅通知 / 模板消息 / 选用模板</a>
     */
    public String addTemplate(String accessToken, AddTemplateRequestPayload requestPayload) {
        requireNonNull(accessToken);
        requireNonNull(requestPayload);

        String body = JacksonUtils.serialize(requestPayload);
        String responseStr = Forest
                .post(config.getWechatGateway() + "/cgi-bin/template/api_add_template")
                .addQuery("access_token", accessToken)
                .addBody(body)
                .contentTypeJson()
                .connectTimeout(config.getConnectTimeout())
                .readTimeout(config.getReadTimeout())
                .executeAsString();
        JsonNode root = JacksonUtils.readTree(responseStr);
        ResponseUtils.findError("选用模板", root, CODE_MAP.get("addTemplate"));
        return root.get("template_id").asText();
    }

    /**
     * 查询拦截的模板消息
     * <p>本接口用于查询被拦截的模板消息
     *
     * @param accessToken    接口调用凭证，可使用 <a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AccessToken.html">access_token</a>、<a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AuthorizerAccessToken.html">authorizer_access_token</a>
     * @param requestPayload 请求体
     * @return 消息信息
     * @see <a href="https://developers.weixin.qq.com/doc/service/api/notify/template/api_queryblocktmplmsg.html">基础消息与订阅通知 / 模板消息 / 查询拦截的模板消息</a>
     */
    public MsgInfo queryBlockTmplMsg(String accessToken, QueryBlockTmplMsgRequestPayload requestPayload) {
        requireNonNull(accessToken);
        requireNonNull(requestPayload);

        String body = JacksonUtils.serialize(requestPayload);
        String responseStr = Forest
                .post(config.getWechatGateway() + "/wxa/sec/queryblocktmplmsg")
                .addQuery("access_token", accessToken)
                .addBody(body)
                .contentTypeJson()
                .connectTimeout(config.getConnectTimeout())
                .readTimeout(config.getReadTimeout())
                .executeAsString();
        JsonNode root = JacksonUtils.readTree(responseStr);
        ResponseUtils.findError("查询拦截的模板消息", root, CODE_MAP.get("queryBlockTmplMsg"));
        return JacksonUtils.deserialize(root.get("msginfo").toPrettyString(), MsgInfo.class);
    }

    /**
     * 删除模板
     * <p>本接口用于删除账号下的指定模板
     *
     * @param accessToken 接口调用凭证，可使用 <a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AccessToken.html">access_token</a>、<a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AuthorizerAccessToken.html">authorizer_access_token</a>
     * @param templateId  公众账号下模板消息ID, 包括类目模板ID
     * @see <a href="https://developers.weixin.qq.com/doc/service/api/notify/template/api_deletetemplate.html">基础消息与订阅通知 / 模板消息 / 删除模板</a>
     */
    public void deleteTemplate(String accessToken, String templateId) {
        requireNonNull(accessToken);
        requireNonNull(templateId);

        String responseStr = Forest
                .post(config.getWechatGateway() + "/cgi-bin/template/del_private_template")
                .addQuery("access_token", accessToken)
                .addBody("template_id", templateId)
                .contentTypeJson()
                .connectTimeout(config.getConnectTimeout())
                .readTimeout(config.getReadTimeout())
                .executeAsString();
        JsonNode root = JacksonUtils.readTree(responseStr);
        ResponseUtils.findError("删除模板", root, CODE_MAP.get("deleteTemplate"));
    }

    /**
     * 获取已选用模板列表
     * <p>本接口用于获取已添加至账号下所有模板列表
     *
     * @param accessToken 接口调用凭证，可使用 <a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AccessToken.html">access_token</a>、<a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AuthorizerAccessToken.html">authorizer_access_token</a>
     * @return 模板列表
     * @see <a href="https://developers.weixin.qq.com/doc/service/api/notify/template/api_getalltemplates.html">基础消息与订阅通知 / 模板消息 / 获取已选用模板列表</a>
     */
    public List<Template> getAllTemplates(String accessToken) {
        requireNonNull(accessToken);

        String responseStr = Forest
                .get(config.getWechatGateway() + "/cgi-bin/template/get_all_private_template")
                .addQuery("access_token", accessToken)
                .contentTypeJson()
                .connectTimeout(config.getConnectTimeout())
                .readTimeout(config.getReadTimeout())
                .executeAsString();
        JsonNode root = JacksonUtils.readTree(responseStr);
        JavaType javaType = JacksonUtils.constructParametricType(List.class, Template.class);
        return JacksonUtils.deserialize(root.get("template_list").toPrettyString(), javaType);
    }

    /**
     * 获取行业信息
     * <p>本接口可获取账号设置的行业信息
     *
     * @param accessToken 接口调用凭证，可使用 <a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AccessToken.html">access_token</a>、<a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AuthorizerAccessToken.html">authorizer_access_token</a>
     * @return 模板列表
     * @see <a href="https://developers.weixin.qq.com/doc/service/api/notify/template/api_getindustry.html">基础消息与订阅通知 / 模板消息 / 获取行业信息</a>
     */
    public Industry getIndustry(String accessToken) {
        requireNonNull(accessToken);

        String responseStr = Forest
                .get(config.getWechatGateway() + "/cgi-bin/template/get_industry")
                .addQuery("access_token", accessToken)
                .contentTypeJson()
                .connectTimeout(config.getConnectTimeout())
                .readTimeout(config.getReadTimeout())
                .executeAsString();
        JsonNode root = JacksonUtils.readTree(responseStr);
        ResponseUtils.findError("获取行业信息", root, CODE_MAP.get("getIndustry"));
        return JacksonUtils.deserialize(responseStr, Industry.class);
    }

    /**
     * 设置所属行业
     * <p>本接口用于修改账号所属行业，修改行业后，你在原有行业中的模板将会被删除。
     *
     * @param accessToken    接口调用凭证，可使用 <a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AccessToken.html">access_token</a>、<a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AuthorizerAccessToken.html">authorizer_access_token</a>
     * @param requestPayload 请求体
     * @see <a href="https://developers.weixin.qq.com/doc/service/api/notify/template/api_setindustry.html">基础消息与订阅通知 / 模板消息 / 设置所属行业</a>
     */
    public void setIndustry(String accessToken, SetIndustryRequestPayload requestPayload) {
        requireNonNull(accessToken);
        requireNonNull(requestPayload);

        String body = JacksonUtils.serialize(requestPayload);
        String responseStr = Forest
                .post(config.getWechatGateway() + "/cgi-bin/template/api_set_industry")
                .addQuery("access_token", accessToken)
                .addBody(body)
                .contentTypeJson()
                .connectTimeout(config.getConnectTimeout())
                .readTimeout(config.getReadTimeout())
                .executeAsString();
        JsonNode root = JacksonUtils.readTree(responseStr);
        ResponseUtils.findError("设置所属行业", root, CODE_MAP.get("setIndustry"));
    }

    private static final Map<String, Map<Long, String>> CODE_MAP;

    static {
        CODE_MAP = new HashMap<>();

        Map<Long, String> codeMap = new HashMap<>();
        codeMap.put(-1L, "系统繁忙，请稍候再试");
        codeMap.put(40001L, "获取 access_token 时 AppSecret 错误，或者 access_token 无效");
        codeMap.put(40003L, "不合法的 OpenID");
        codeMap.put(40008L, "不合法的消息类型");
        codeMap.put(40013L, "不合法的 AppID");
        codeMap.put(40036L, "不合法的 template_id 长度");
        codeMap.put(40037L, "不合法的 template_id");
        codeMap.put(40039L, "不合法的 URL 长度");
        codeMap.put(40249L, "禁止发送营销内容");
        codeMap.put(43116L, "模板被限制下发");
        codeMap.put(47003L, "参数格式错误");
        CODE_MAP.put("sendTemplateMessage", Collections.unmodifiableMap(codeMap));

        codeMap = new HashMap<>();
        codeMap.put(-1L, "系统繁忙，请稍候再试");
        codeMap.put(40001L, "获取 access_token 时 AppSecret 错误，或者 access_token 无效");
        codeMap.put(40037L, "不合法的 template_id");
        codeMap.put(40246L, "需要传入正确的keyword_name_list");
        codeMap.put(40247L, "请使用类目模板库ID进行添加");
        CODE_MAP.put("addTemplate", Collections.unmodifiableMap(codeMap));
    }

}
